/**
 * @(#)SimulatorServiceImpl.java 2017年2月14日
 * Copyright(C) 2017 ZHEXIN IT CO.,LTD. All rights reserved.
 */
package org.jsbd.boss.monitor.service;


import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.jsbd.boss.common.monitor.MonitorContentTemplate;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.dao.IDmChannelRetentDAO;
import org.jsbd.boss.dao.IWeiboUserDAO;
import org.jsbd.boss.dao.impl.WeiboRetentDAOImpl;
import org.jsbd.boss.domian.SimulatorInfoVo;
import org.jsbd.boss.domian.channel.ApkTaskInfo;
import org.jsbd.boss.domian.channel.ChannelApkConfig;
import org.jsbd.boss.domian.channel.TaskQueryVo;
import org.jsbd.boss.domian.channel.WeiBoVo;
import org.jsbd.boss.domian.monitor.MonitorSimulatorNumInfo;
import org.jsbd.boss.service.IApkTaskQueryService;
import org.jsbd.boss.service.SimulatorManager;
import org.jsbd.boss.service.impl.CacheParamManager;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.jsbd.boss.util.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;

/**
 * 模拟器数量监控
 *
 * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
 * @version 1.0.0
 */

@Service("monitorSimulatorNumService")
public class MonitorSimulatorNumServiceImpl extends MonitorBaseService {
    public static final Logger LOGGER = LoggerFactory.getLogger(MonitorSimulatorNumServiceImpl.class);

    /**
     * 单位秒
     */
    private static final int ONE_DAY = 24 * 3600;
    /**
     * 上一次执行情况的缓存key
     */
    public static final String LAST_MARK_TASK_NUM_MAP = "LAST_MARK_TASK_NUM_MAP";

    @Autowired
    private IApkTaskQueryService apkTaskQueryService;
    @Autowired
    private RedisClient<String, Object> redisClient;
    @Resource
    private IDmChannelRetentDAO dmChannelRetentDAO;
    @Autowired
    private IWeiboUserDAO weiboUserDAO;
    @Autowired
    private WeiboRetentDAOImpl weiboRetentDao;

    @Resource
    private CacheParamManager cacheParamManager;

    @Resource
    private SimulatorManager simulatorManager;

    @Override
    public Map<String, String> getMonitorContent(List<ChannelApkConfig> apkList, Map<String, ChannelApkConfig> apkConfigMap, Map<String, Object> conditionMap) {
        //存放最终数据的列表
        List<MonitorSimulatorNumInfo> resultList = new ArrayList<MonitorSimulatorNumInfo>();

        //在线模拟器情况
        Map<String, Integer> onlineSimulatorNumMap = getOnlineSimulatorNumMap();

        //mark需要的模拟器情况,并将结果放入resultList
        Map<String, Long> verSimulatorMap = getSimulatorNumList(apkList, resultList, onlineSimulatorNumMap);
        Map<String, String> resultMap = getMonitorResultMap(resultList, MonitorContentTemplate.NUM_TABLE_HEADER, "SIMULATOR_NUM_MONITOR");
        String content = resultMap.get("content");
        if (StringUtils.isBlank(content)) {
            return resultMap;
        }

        StringBuffer buffer = new StringBuffer();

        buffer.append(getSimulatorReport(verSimulatorMap, onlineSimulatorNumMap));
        buffer.append("<hr>");
        buffer.append(content);

        resultMap.put("content", buffer.toString());

        return resultMap;
    }

    /**
     * 模拟器数量报告
     *
     * @param verSimulatorMap
     * @param onlineSimulatorNumMap
     * @return
     * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
     */
    public String getSimulatorReport(Map<String, Long> verSimulatorMap, Map<String, Integer> onlineSimulatorNumMap) {

        StringBuffer buffer = new StringBuffer();

        for (Entry<String, Long> entity : verSimulatorMap.entrySet()) {
            Integer onlineNum = onlineSimulatorNumMap.get(entity.getKey());
            if (onlineNum == null) {
                onlineNum = 0;
            }

            Integer unbusyNum = onlineSimulatorNumMap.get(entity.getKey() + "_unbusy");
            if (unbusyNum == null) {
                unbusyNum = 0;
            }

            String color = "";
            Integer needNum = onlineSimulatorNumMap.get(entity.getKey() + "_need");
            if (needNum == null) {
                needNum = 0;
            }

            if (needNum > 0) {
                color = "color:red";
            }


            String content = MonitorContentTemplate.SIMULATOR_ALARM_INFO.replace("{vername}", entity.getKey())
                    .replace("{needNum}", needNum + "")
                    .replace("{onlineNum}", onlineNum + "")
                    .replace("{unbusyNum}", unbusyNum + "")
                    .replace("{color}", color);

            buffer.append(content);
            buffer.append("<br>");
        }
        return buffer.toString();
    }

    public Map<String, Long> getSimulatorNumList(List<ChannelApkConfig> apkList,
                                                 List<MonitorSimulatorNumInfo> resultList, Map<String, Integer> onlineSimulatorNumMap) {
        //存放对应版本剩余任务完成需要时间（单位秒）
        Map<String, Long> verSimulatorMap = new HashMap<String, Long>();
        TaskQueryVo queryVo = new TaskQueryVo();
        queryVo.setCurrentDate(edu.hziee.common.lang.DateUtil.formatDate(new Date(), "yyyyMMdd"));
        Map<String, String> monitorConfigMap = getMonitorConfig();


        List<ApkTaskInfo> currList = dmChannelRetentDAO.selectCountNewTask(queryVo);
        List<ApkTaskInfo> retentList = dmChannelRetentDAO.selectCountRetentTask(queryVo);

        //添加微博、微信
        addWeixinAndWeiBo(currList, retentList, queryVo.getCurrentDate());

        Map<String, ApkTaskInfo> apkinfomap = new HashMap<String, ApkTaskInfo>();
        Map<String, ApkTaskInfo> retentInfomap = new HashMap<String, ApkTaskInfo>();
        for (ApkTaskInfo apkTaskInfo : currList) {
            apkinfomap.put(apkTaskInfo.getMark(), apkTaskInfo);
        }
        for (ApkTaskInfo apkTaskInfo : retentList) {
            retentInfomap.put(apkTaskInfo.getMark(), apkTaskInfo);
        }

        //填充每个mark对应的任务数以及需要的模拟器数量
        apkList = apkTaskQueryService.queryApkConfigList(apkList);
        for (ChannelApkConfig apkConfig : apkList) {
            MonitorSimulatorNumInfo taksInfo = new MonitorSimulatorNumInfo();
            resultList.add(taksInfo);

            taksInfo.setApkName(apkConfig.getApkName());
            taksInfo.setChannelName(apkConfig.getChannelName());
            taksInfo.setMark(apkConfig.getMark());
            taksInfo.setNewTaskNum(apkConfig.getNewTaskNum());
            taksInfo.setRetentTaskNum(apkConfig.getRetentTaskNum());
            taksInfo.setNewTaskTime(apkConfig.getNewTaskTime());
            taksInfo.setRetentTaskTime(apkConfig.getRetentTaskTime());

            int needSimulator = taksInfo.getPlanNewTaskTotal() + taksInfo.getPlanRetentTaskTotal()
                    - taksInfo.getHandlerNewTask() - taksInfo.getHandlerRetentTask();

            taksInfo.setNeedSimulators(needSimulator);
            taksInfo.setSysVer(apkConfig.getSysVer());

            ApkTaskInfo apkTaskInfo = apkinfomap.get(taksInfo.getMark());
            if (apkTaskInfo != null) {
                taksInfo.setRealexeNum(apkTaskInfo.getExecTotal());
                taksInfo.setMobileNum(apkTaskInfo.getMobileNum());
                if (taksInfo.getNewTaskTotal() > 0) {
                    BigDecimal b = new BigDecimal((double) taksInfo.getRealexeNum() / taksInfo.getNewTaskTotal());
                    taksInfo.setExecRate(b.setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue());
                } else {
                    taksInfo.setExecRate(0d);
                }
            }

            ApkTaskInfo apkRetentTaskInfo = retentInfomap.get(taksInfo.getMark());
            if (apkRetentTaskInfo != null) {
                taksInfo.setRetentRealexeNum(apkRetentTaskInfo.getExecTotal());
            }

            taksInfo.setRealExeNumStr(taksInfo.getRealexeNum() + "|" + taksInfo.getRetentRealexeNum());

            incRemainTime(verSimulatorMap, taksInfo);
            //当任务做不完的时候，进行判断
            if (taksInfo.getNeedSimulators() > 10) {
                validateRow(taksInfo, onlineSimulatorNumMap, verSimulatorMap, monitorConfigMap);
            }
        }

        validateSimulatorNum(resultList, onlineSimulatorNumMap, verSimulatorMap);


        Collections.sort(resultList, new SimulatorNumInfoComparator());

        return verSimulatorMap;
    }


    private void addWeixinAndWeiBo(List<ApkTaskInfo> currList, List<ApkTaskInfo> retentList, String date) {
        WeiBoVo weiBoVo = new WeiBoVo();
        weiBoVo.setCurrentDay(date);

        List<WeiBoVo> weiBoNewList = weiboUserDAO.getWeiBoHandler(weiBoVo);
        List<WeiBoVo> weiBoVoRetentList = weiboRetentDao.getWeiBoHandler(weiBoVo);

        for (WeiBoVo vo : weiBoNewList) {
            ApkTaskInfo apkVo = new ApkTaskInfo();
            apkVo.setMark(vo.getMark());
            apkVo.setExecTotal(vo.getCount());
            apkVo.setMobileNum(0);
            currList.add(apkVo);
        }

        for (WeiBoVo vo : weiBoVoRetentList) {
            ApkTaskInfo apkVo = new ApkTaskInfo();
            apkVo.setMark(vo.getMark());
            apkVo.setExecTotal(vo.getCount());
            apkVo.setMobileNum(0);
            retentList.add(apkVo);
        }

    }

    /**
     * 统计执行量：比较前一段时间跟当前时间获取的新增与留存量是否一致，如果一致表示没有执行，说明有问题
     *
     * @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
     */
    private void statHandlerTaskNum(Map<String, String> lastMap, MonitorSimulatorNumInfo taksInfo) {
        String taskNums = lastMap.get(taksInfo.getMark());
        lastMap.put(taksInfo.getMark(), taksInfo.getHandlerNewTask() + ","
                + taksInfo.getHandlerRetentTask() + "," + taksInfo.getRealexeNum() + "," + taksInfo.getRetentRealexeNum());
        if (taskNums == null) {
            return;
        }

        String[] taskNumArr = taskNums.split(",");
        if (taskNumArr.length < 4) {
            return;
        }

        int lastNewTaskNum = Integer.valueOf(taskNumArr[0]);
        int lastRetentTaskNum = Integer.valueOf(taskNumArr[1]);
        int lastRealExeNum = Integer.valueOf(taskNumArr[2]);
        int lastRetentRealExeNum = Integer.valueOf(taskNumArr[3]);

        int newRemain = taksInfo.getHandlerNewTask() - lastNewTaskNum;
        int retentRemain = taksInfo.getHandlerRetentTask() - lastRetentTaskNum;

        int realExeNum = taksInfo.getRealexeNum() - lastRealExeNum;
        int retentRealExeNum = taksInfo.getRetentRealexeNum() - lastRetentRealExeNum;

        taksInfo.setHandlerNumStr(newRemain + "|" + retentRemain);

        if (taksInfo.getPlanNewTaskTotal() > taksInfo.getHandlerNewTask()) {
            if (newRemain == 0) {
                taksInfo.setColour("yellow");
            }
        }

        if (taksInfo.getPlanRetentTaskTotal() > taksInfo.getHandlerRetentTask()) {
            if (retentRemain == 0) {
                taksInfo.setColour("yellow");
            }
        }

        if (newRemain > 0) {
            if (realExeNum < 1) {
                taksInfo.setColour("yellow");
                taksInfo.setErrorMsg("新增有下发量，没有执行量");
            }
        }

        if (retentRemain > 0) {
            if (retentRealExeNum < 1) {
                taksInfo.setColour("yellow");
                taksInfo.setErrorMsg(" 留存有下发量，没有执行量");
            }
        }

    }

    private Map<String, String> validateSimulatorNum(List<MonitorSimulatorNumInfo> resultList, Map<String, Integer> onlineSimulatorNumMap, Map<String, Long> verSimulatorMap) {

        Map<String, String> resultMap = new HashMap<String, String>();
        long remainSecond = DateUtil.getRemainSecondForDay();

        for (Entry<String, Long> entity : verSimulatorMap.entrySet()) {
            Integer onlineNum = onlineSimulatorNumMap.get(entity.getKey());
            if (onlineNum == null) {
                onlineNum = 0;
            }

            int index = 1;
            if (entity.getValue() % remainSecond == 0) {
                index = 0;
            }

            long needNum = (entity.getValue() / remainSecond) + index;
            //加入在线数小于需要的模拟器数，则标记为缺少模拟器
            if (onlineNum < needNum) {
                resultMap.put(entity.getKey() + "_color", "red");
                onlineSimulatorNumMap.put(entity.getKey() + "_need", Integer.valueOf((needNum - onlineNum) + ""));
            }
        }


        String key = LAST_MARK_TASK_NUM_MAP + DateUtil.getCurrentDateStr();

        Map<String, String> lastMap = (Map<String, String>) redisClient.get(key);
        if (lastMap == null) {
            lastMap = new HashMap<String, String>();
        }

        for (MonitorSimulatorNumInfo taskInfo : resultList) {

            statHandlerTaskNum(lastMap, taskInfo);
            //如果没有任务数，则说明执行的正常
            if (taskInfo.getNeedSimulators() < 1) {
                taskInfo.setColour("");
            }

            if (taskInfo.getNeedSimulators() < 10) {
                continue;
            }

            String color = resultMap.get(taskInfo.getSysVer() + "_color");
            if (StringUtils.isNotBlank(color)) {
                if (StringUtils.isBlank(taskInfo.getColour())) {
                    taskInfo.setColour(color);
                }
                taskInfo.setErrorMsg("模拟器数量不够");
            }
        }

        redisClient.put(key, lastMap, ONE_DAY);

        return resultMap;
    }

    /**
     * 校验每行数据是否合法，同时统计剩余任务耗时
     *
     * @param taksInfo
     * @param onlineSimulatorNumMap
     * @param verSimulatorMap
     * @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
     */
    private void validateRow(MonitorSimulatorNumInfo taksInfo, Map<String, Integer> onlineSimulatorNumMap,
                             Map<String, Long> verSimulatorMap, Map<String, String> monitorConfigMap) {

        //判断在线用户数不够的情况，模拟器数量由外部统一判断
        int planNewTaskTotal = taksInfo.getPlanNewTaskTotal();
        int handlernewTask = taksInfo.getHandlerNewTask();

        int planRetentTaskTotal = taksInfo.getPlanRetentTaskTotal();
        int handlerRetentTask = taksInfo.getHandlerRetentTask();

        if (planNewTaskTotal == 0) {
            planNewTaskTotal = 1;
        }
        if (planRetentTaskTotal == 0) {
            planRetentTaskTotal = 1;
        }

        double newRate = handlernewTask * 1.0 / planNewTaskTotal;
        double retentRate = handlerRetentTask * 1.0 / planRetentTaskTotal;


        String newRetentRate = monitorConfigMap.get("new_retent_rate");
        if (StringUtils.isBlank(newRetentRate)) {
            newRetentRate = "0.1";
        }

        //当有新增与留存任务时，如果新增执行比例大于留存一定比例，则任务在线用户数不够
        if (newRate > 0 && retentRate > 0 & (retentRate - newRate > Double.valueOf(newRetentRate))) {
            taksInfo.setColour("red");
            taksInfo.setErrorMsg("如果没有改过量，说明在线用户数不够");
            return;
        }

        String maxRemainNewTaskRate = monitorConfigMap.get("max_remain_new_task_rate");
        if (StringUtils.isBlank(maxRemainNewTaskRate)) {
            maxRemainNewTaskRate = "0.8";
        }

        if (newRate < Double.valueOf(maxRemainNewTaskRate)) {
            //检测模拟器能够最大空闲的量
            String maxUnbusyNum = monitorConfigMap.get("max_unbusy_simulator_num");
            if (StringUtils.isBlank(maxUnbusyNum)) {
                maxUnbusyNum = "30";
            }

            Integer num = onlineSimulatorNumMap.get(taksInfo.getSysVer() + "_unbusy");
            if (num != null && num > Integer.valueOf(maxUnbusyNum)) {
                taksInfo.setColour("red");
                taksInfo.setErrorMsg("如果没有改过量，说明在线用户数不够");
            }
        }
    }

    /**
     * 统计每个版本剩余任务最少需要多少时间完成（单位秒）
     *
     * @param verSimulatorMap
     * @param taksInfo
     * @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
     */
    private void incRemainTime(Map<String, Long> verSimulatorMap, MonitorSimulatorNumInfo taksInfo) {
        Long time = verSimulatorMap.get(taksInfo.getSysVer());
        if (time == null) {
            time = 0L;
        }

        //跟总量比，没有做的量还需要多少时间
        long totalRemainNewTask = (taksInfo.getNewTaskTotal() - taksInfo.getHandlerNewTask()) * taksInfo.getNewTaskTime();
        long totalRemainRetentTask = (taksInfo.getRetentTaskTotal() - taksInfo.getHandlerRetentTask()) * taksInfo.getRetentTaskTime();
        long needTotalTime = totalRemainNewTask + totalRemainRetentTask;
        verSimulatorMap.put(taksInfo.getSysVer(), time + needTotalTime);
    }

    @Deprecated
    private void validateOk(MonitorSimulatorNumInfo taksInfo, String itemValue, Integer unbusyNum) {
        if (!NumberUtils.isNumber(itemValue)) {
            return;
        }

        if (taksInfo.getNeedSimulators() > Integer.valueOf(itemValue)) {
            taksInfo.setColour("red");
            if (unbusyNum == null) {
                unbusyNum = 0;
            }

            if (unbusyNum > 0) {
                taksInfo.setErrorMsg("在线用户数不够");
            } else {
                taksInfo.setErrorMsg("模拟器数量不够");
            }
        }
    }

    /*
     * 获取现网在线的模拟器数量，并按版本划分
     */
    public Map<String, Integer> getOnlineSimulatorNumMap() {
        Map<String, Integer> onlineSimulatorNumMap = new HashMap<String, Integer>();

        long now = System.currentTimeMillis();
        Object expreTime = cacheParamManager.getParamValue(CacheKey.ONLINE_SIMULATORS_TIME);
        long startTime = 0;
        if (expreTime != null) {
            startTime = now - Integer.parseInt(expreTime.toString()) * 1000;
        }
        List<String> invalidList = new ArrayList<String>();
        Map<Object, Object> simulatorMap = simulatorManager.getAllSimulatorMap();
        if (simulatorMap == null || simulatorMap.isEmpty()) {
            return onlineSimulatorNumMap;
        }

        for (Entry<Object, Object> entity : simulatorMap.entrySet()) {
            SimulatorInfoVo simulatorInfoVo = (SimulatorInfoVo) entity.getValue();
            //超时的就不要了
            if (simulatorInfoVo.getCurrentTime() < startTime) {
                invalidList.add(simulatorInfoVo.getUuid());
                continue;
            }
            Integer num = onlineSimulatorNumMap.get(simulatorInfoVo.getSysVer());
            Integer numBusy = onlineSimulatorNumMap.get(simulatorInfoVo.getSysVer() + "_unbusy");
            if (num == null) {
                num = 0;
            }
            if (numBusy == null) {
                numBusy = 0;
                onlineSimulatorNumMap.put(simulatorInfoVo.getSysVer() + "_unbusy", numBusy);
            }

            if (!simulatorInfoVo.isBusy()) {
                onlineSimulatorNumMap.put(simulatorInfoVo.getSysVer() + "_unbusy", numBusy + 1);
            }

            onlineSimulatorNumMap.put(simulatorInfoVo.getSysVer(), num + 1);
        }

        if (!invalidList.isEmpty()) {
            for(String uuid : invalidList){
                String simulatorHashKey = simulatorManager.getSimulatorHashKey(uuid);
                redisClient.removeHash(simulatorHashKey, uuid);
            }
//            redisClient.removeHash(CacheKey.ONLINE_SIMULATORS_HASH_KEY, invalidList.toArray(new String[0]));
        }

        return onlineSimulatorNumMap;
    }


    static class SimulatorNumInfoComparator implements Comparator<MonitorSimulatorNumInfo> {
        @Override
        public int compare(MonitorSimulatorNumInfo arg0, MonitorSimulatorNumInfo arg1) {
            int data1 = Integer.valueOf(arg1.getNeedSimulators()).intValue();
            int data0 = Integer.valueOf(arg0.getNeedSimulators()).intValue();
            if (data1 > data0) {
                return 1;
            }
            if (data1 < data0) {
                return -1;
            }
            return 0;
        }
    }


}
